package com.xbongbong.paas.log;

import com.xbongbong.paas.log.enums.FilterOperatorEnum;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.LogConstant;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 日志操作相关工具类
 * @author kaka
 * @date 2019/1/21 15:14
 * @since v1.0
 * @version v1.0
 */
public class MongoLogUtil {

    private static final Logger LOG = LoggerFactory.getLogger(MongoLogUtil.class);


    /**
     * 构造mongo query实体
     * @param conditions 查询条件
     * @return 构造好的query
     */
    public static Query formatQuery(MongoQueryConditions conditions,boolean isExport) throws IllegalArgumentException {
        String corpid = conditions.getCorpid();
        //不允许corpid为空的情况,查询至少要带上corpid
        if (StringUtil.isEmpty(corpid)) {
            throw new IllegalArgumentException(String.format(I18nStringConstant.CANNOT_BE_EMPTY, "corpid"));
        }
        Integer maxPageSize = LogConstant.MAX_LOG_PAGE_SIZE;
        if(isExport){
            //导出需求吧pageSize放大
            maxPageSize = LogConstant.MAX_LOG_EXPORT_PAGE_SIZE;
        }
        Query query = new Query();
        // corpid条件
        Criteria corpidCri = Criteria.where("corpid").is(corpid);
        query.addCriteria(corpidCri);
        List<MongoQueryItem> items = conditions.getConditions();
        Map<String, Criteria> criMap = new HashMap<>(16);
        if (items.size() > 0) {
            for (MongoQueryItem item : items) {
                // 为了处理同一field多个条件问题，比如 addTime >= 1548044102 && addTime < 1548044385
                Criteria criteria = getCriteria(item, criMap);
                criMap.put(item.getField(), criteria);
            }
        }

        for (Criteria criteria : criMap.values()) {
            query.addCriteria(criteria);
        }


        // 分页相关
        if (conditions.getSkip() > 0) {
            query.skip(conditions.getSkip());
        }
        if (conditions.getLimit() > 0 && conditions.getLimit() <= maxPageSize) {
            query.limit(conditions.getLimit());
        } else {
            query.limit(PageHelper.PAGE_SIZE);
        }

        // 排序
        List<String> sortItems = conditions.getSorts();
        if(sortItems.size() > 0) {
            for (String sortItem : sortItems) {
                if(!StringUtil.isEmpty(sortItem)) {
                    String[] sortTuple = sortItem.split(" ");
                    if(sortTuple.length < 1 || sortTuple.length > 2) {
                        continue;
                    }
                    if(sortTuple.length == 1) {
                        query.with(Sort.by(Sort.Direction.ASC, sortTuple[0]));
                    } else {
                        String itemOrder = sortTuple[1].toUpperCase();
                        if("DESC".equals(itemOrder)) {
                            query.with(Sort.by(Sort.Direction.DESC, sortTuple[0]));
                        } else {
                            query.with(Sort.by(Sort.Direction.ASC, sortTuple[0]));
                        }
                    }
                }
            }
        }

        return query;
    }


    /**
     * 将 MongoQueryItem 格式化为 Criteria
     * @param item MongoQueryItem实体
     * @param criMap 已拼装参数的Criteria Map, 其中key为field, 值为对应的Criteria
     * @return 格式化好的Criteria
     */
    private static Criteria getCriteria(MongoQueryItem item, Map<String, Criteria> criMap) {
        FilterOperatorEnum operatorEnum = item.getOperator();
        String field = item.getField();
        Object value = item.getValue();

        Criteria criteria = criMap.get(field);
        if (criteria == null) {
            criteria = Criteria.where(field);
        }

        switch (operatorEnum) {
            case EQUAL:
                criteria.is(value);
                break;
            case NOT_EQUAL:
                criteria.ne(value);
                break;
            case GREAT_THAN:
                criteria.gt(value);
                break;
            case LESS_THAN:
                criteria.lt(value);
                break;
            case GREAT_THAN_OR_EQ:
                criteria.gte(value);
                break;
            case LESS_THAN_OR_EQ:
                criteria.lte(value);
                break;
            case IN:
                criteria.in((Collection)value);
                break;
            case NOT_IN:
                criteria.nin((Collection)value);
                break;
            case EXISTS:
                criteria.exists(Boolean.valueOf((String)value));
                break;
            case CONTAINS:
                // 防止模糊搜索时，特殊字符报错
                String keyword = escapeExprSpecialWord((String)value);
                criteria.regex(keyword);
                break;
            default:
                criteria.is(value);
        }

        return criteria;
    }


    /**
     * 根据前端参数构造操作日志的查询条件 MongoQueryConditions
     * 用于 {@link com.xbongbong.paas.log.model.impl.OperateLogModelImpl#findEntitys(Map)}
     * 和 {@link com.xbongbong.paas.log.model.impl.OperateLogModelImpl#getEntitysCount(Map)}
     * @param param 前端查询参数
     * @return 返回登录日志的查询条件实体 MongoQueryConditions
     */
    public static MongoQueryConditions formatOperateLogQueryConditions(Map<String, Object> param) {
        //corpid为空的情况通过底层调用方法处理掉
        String corpid = (String)param.get("corpid");
        Integer page = (Integer)param.get("page");
        Integer start = (Integer)param.get("start");
        Integer pageNum = (Integer)param.get("pageNum");
        Long maxTime = (Long)param.get("operateMaxTime");
        Long minTime = (Long)param.get("operateMinTime");

        Integer moduleType = (Integer)param.get("moduleType");
        String moduleName = null;
        if(moduleType != null){
            moduleName = OperateModuleTypeEnum.getByCode(moduleType).getName();
        }
        Integer opType = (Integer)param.get("opType");
        String opTypeName = null;
        if(opType != null){
            opTypeName = OperateTypeEnum.getByCode(opType).getName();
        }
        List<String> opUserIds = (List<String>)param.get("opUserIds");
        String opObjectName = (String)param.get("operateObject");
        String memo = (String)param.get("memoLike");
        String orderByStr = (String)param.get("orderByStr");

        if(page < 1) {
            page = 1;
        }
        if(pageNum == null || pageNum < 0) {
            pageNum = 15;
        }
        //计算偏移量
        //计算偏移量
        if(start == null || start < 0) {
            start = (page - 1) * pageNum;
        }

        MongoQueryConditions conditions = new MongoQueryConditions(start, pageNum);
        //corpid额外设置
        conditions.setCorpid(corpid);
        if(!StringUtil.isEmpty(moduleName)) {
            conditions.addCondition(new MongoQueryItem("moduleName", FilterOperatorEnum.EQUAL, moduleName));
        }
        if(!StringUtil.isEmpty(opTypeName)) {
            conditions.addCondition(new MongoQueryItem("opTypeName", FilterOperatorEnum.EQUAL, opTypeName));
        }
        if(opUserIds != null && opUserIds.size() > 0) {
            conditions.addCondition(new MongoQueryItem("opUserId", FilterOperatorEnum.IN, opUserIds));
        }
        if(!StringUtil.isEmpty(opObjectName)) {
            conditions.addCondition(new MongoQueryItem("opObjectName", FilterOperatorEnum.CONTAINS, opObjectName.trim()));
        }
        if(!StringUtil.isEmpty(memo)) {
            conditions.addCondition(new MongoQueryItem("memo", FilterOperatorEnum.CONTAINS, memo.trim()));
        }
        if(maxTime != null) {
            conditions.addCondition(new MongoQueryItem("addTime", FilterOperatorEnum.LESS_THAN, maxTime));
        }
        if(minTime != null) {
            conditions.addCondition(new MongoQueryItem("addTime", FilterOperatorEnum.GREAT_THAN_OR_EQ, minTime));
        }

        //排序字段
        if(!StringUtil.isEmpty(orderByStr)) {
            conditions.formatOrderByStr(orderByStr);
        }

        return conditions;
    }

    /**
     * 根据前端参数构造登录日志的查询条件 MongoQueryConditions
     * 用于 {@link com.xbongbong.paas.log.model.impl.LoginLogModelImpl#findEntitys(Map)} 和
     * {@link com.xbongbong.paas.log.model.impl.LoginLogModelImpl#getEntitysCount(Map)}
     * @param param 前端查询参数
     * @return 返回登录日志的查询条件实体 MongoQueryConditions
     */
    public static MongoQueryConditions formatLoginLogQueryConditions(Map<String, Object> param) {
        //corpid为空的情况通过底层调用方法处理掉
        String corpid = (String)param.get("corpid");
        Integer page = (Integer)param.get("page");
        Integer start = (Integer)param.get("start");
        Integer pageNum = (Integer)param.get("pageNum");
        List<String> loginUserIds = (List<String>)param.get("loginUserIds");
        String userNameLike = (String)param.get("userNameLike");
        Long maxTime = (Long)param.get("operateMaxTime");
        Long minTime = (Long)param.get("operateMinTime");
        String orderByStr = (String)param.get("orderByStr");
        String platform = (String)param.get("platform");

        if(page == null || page < 1) {
            page = 1;
        }
        if(pageNum == null || pageNum < 0) {
            pageNum = 15;
        }
        //计算偏移量
        if(start == null || start < 0) {
            start = (page - 1) * pageNum;
        }

        MongoQueryConditions conditions = new MongoQueryConditions(start, pageNum);
        //corpid额外设置
        conditions.setCorpid(corpid);
        if(loginUserIds != null && loginUserIds.size() > 0) {
            conditions.addCondition(new MongoQueryItem("userId", FilterOperatorEnum.IN, loginUserIds));
        }
        if(!StringUtil.isEmpty(userNameLike)) {
            conditions.addCondition(new MongoQueryItem("userName", FilterOperatorEnum.CONTAINS, userNameLike.trim()));
        }
        if(maxTime != null) {
            conditions.addCondition(new MongoQueryItem("addTime", FilterOperatorEnum.LESS_THAN, maxTime));
        }
        if(minTime != null) {
            conditions.addCondition(new MongoQueryItem("addTime", FilterOperatorEnum.GREAT_THAN_OR_EQ, minTime));
        }
        if (platform != null) {
            conditions.addCondition(new MongoQueryItem("platform", FilterOperatorEnum.EQUAL, platform));
        }
        //排序字段
        if(!StringUtil.isEmpty(orderByStr)) {
            conditions.formatOrderByStr(orderByStr);
        }

        return conditions;
    }

    /**
     * 转义正则特殊字符 （$()*+.[]?\^{},|）
     *
     * @param keyword
     * @return
     */
    private static String escapeExprSpecialWord(String keyword) {
        if (StringUtils.isNotBlank(keyword)) {
            String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" };
            for (String key : fbsArr) {
                if (keyword.contains(key)) {
                    keyword = keyword.replace(key, "\\" + key);
                }
            }
        }
        return keyword;
    }
}
